AWS Step FunctionsをAWS SDK For Pythonから使ってみる #reinvent
AWS re:Invent 2016 Keynoteで発表された、ビジュアライズなワークフローを使用して、分散アプリケーションと Microservicesのコンポーネントを簡単にコーディネート出来るサービス『Step Functions』をAWS SDK for Python から使ってみます。
チュートリアル "Getting Started with AWS Step Functions" にある no-op な Pass ステートを利用したステートマシンを AWS SDK For Python から作成し、実行します。
Getting Started with AWS Step Functions - Developer Guide
AWS CLI向けは次の記事を参照下さい。
AWS Step FunctionsをAWS CLIから使ってみる #reinvent
1.ステートマシンの作成
ステートマシンの定義
"Type" が "Pass" の no-op なステートマシンを作成します。
JSON ベースの Amazon States Language でステートマシンを定義します。
{ "Comment": "A Hello World example of the Amazon States Language using an AWS Lambda Function", "StartAt": "HelloWorld", "States": { "HelloWorld": { "Type": "Pass", "Result": "Hello World!", "End": true } } }
ステートマシンの実行用 IAM ロール
ステートマシンの実行用に自動生成されたIAM Roleを指定します。 IAM Role の ARN を控えておいて下さい。
自動生成されたロールは以下のとおりです。 Step Function が Lambda を呼び出せるようになっていますが、今回は Lambda 連携は行いません。
Permissions
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": "*" } ] }
Trust Relationships
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "states.eu-west-1.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
ステートマシンの作成
最後の CreateStateMachine
API でステートマシンを作成します。
引数 | 意味 |
Name | ステートマシン名。リージョン毎にユニーク |
Definition | ステートマシンのAmazon States Languageによる定義 |
RoleArn | ステートマシンが利用するIAMロールのARN |
import boto3 client = boto3.client('stepfunctions') client.create_state_machine( name="foo", definition=open("definition.json").read(), roleArn="arn:aws:iam::123456789012:role/service-role/StatesExecutionRole-eu-west-1" )
RESPONSE
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '109', 'content-type': 'application/x-amz-json-1.0', 'x-amzn-requestid': 'd72f1e08-c500-11e6-a0eb-016b0ac9b948'}, 'HTTPStatusCode': 200, 'RequestId': 'd72f1e08-c500-11e6-a0eb-016b0ac9b948', 'RetryAttempts': 0}, u'creationDate': datetime.datetime(2016, 12, 18, 10, 3, 28, 295000, tzinfo=tzlocal()), u'stateMachineArn': u'arn:aws:states:eu-west-1:123456789012:stateMachine:foo'}
作成したステートマシンを DescribeStateMachine
API で確認しましょう。
describe 対象は ステートマシンの ARN を指定します。
client.describe_state_machine(stateMachineArn='arn:aws:states:eu-west-1:123456789012:stateMachine:foo')
RESPONSE
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '518', 'content-type': 'application/x-amz-json-1.0', 'x-amzn-requestid': '1cf27650-c501-11e6-a642-43c1a53708be'}, 'HTTPStatusCode': 200, 'RequestId': '1cf27650-c501-11e6-a642-43c1a53708be', 'RetryAttempts': 0}, u'creationDate': datetime.datetime(2016, 12, 18, 10, 2, 17, 132000, tzinfo=tzlocal()), u'definition': u'{\n "Comment": "A Hello World example of the Amazon States Language using an AWS Lambda Function",\n "StartAt": "HelloWorld",\n "States": {\n "HelloWorld": {\n "Type": "Pass",\n "Result": "Hello World!",\n "End": true\n }\n }\n}\n', u'name': u'foo', u'roleArn': u'arn:aws:iam::123456789012:role/service-role/StatesExecutionRole-eu-west-1', u'stateMachineArn': u'arn:aws:states:eu-west-1:123456789012:stateMachine:foo', u'status': u'ACTIVE'}
ListSateMachines
API を使うと、ステートマシン一覧を取得できます。
import boto3 client = boto3.client('stepfunctions') client.list_state_machines()
RESPONSE
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '265', 'content-type': 'application/x-amz-json-1.0', 'x-amzn-requestid': 'c60e66be-c4a1-11e6-a0c5-453e0e1f9c8f'}, 'HTTPStatusCode': 200, 'RequestId': 'c60e66be-c4a1-11e6-a0c5-453e0e1f9c8f', 'RetryAttempts': 0}, u'stateMachines': [{u'creationDate': datetime.datetime(2016, 12, 17, 22, 41, 2, 537000, tzinfo=tzlocal()), u'name': u'foo', u'stateMachineArn': u'arn:aws:states:eu-west-1:123456789012:stateMachine:foo'}, {u'creationDate': datetime.datetime(2016, 12, 17, 22, 37, 14, 22000, tzinfo=tzlocal()), u'name': u'foo2', u'stateMachineArn': u'arn:aws:states:eu-west-1:123456789012:stateMachine:foo'}]}
2.ステートマシンの実行
作成したステートマシンを実行します。
実行用入力データを JSON で用意します。
{ "Comment": "Insert your JSON here" }
ステートマシンは StartExecution
API で呼び出します。
import boto3 client = boto3.client('stepfunctions') client.start_execution( **{ 'input' : open('input.json').read(), 'stateMachineArn' : 'arn:aws:states:eu-west-1:123456789012:stateMachine:foo' } )
RESPONSE
{u'startDate': datetime.datetime(2016, 12, 18, 10, 8, 10, 135000, tzinfo=tzlocal()), 'ResponseMetadata': {'RetryAttempts': 0, 'HTTPStatusCode': 200, 'RequestId': '7f297002-c501-11e6-aba3-974b2c19db6e', 'HTTPHeaders': {'x-amzn-requestid': '7f297002-c501-11e6-aba3-974b2c19db6e', 'content-length': '136', 'content-type': 'application/x-amz-json-1.0'}}, u'executionArn': u'arn:aws:states:eu-west-1:123456789012:execution:foo:28c88d3b-9d58-4d23-840d-34329fe1951e'}
本当は client.start_execution(input = open('input.json').read(), 'stateMachineArn' = 'XXX')
のようにしたいところですが、input
は Python のキーワードのため、引数で利用しようとするとシンタックスエラーになります。そのワークアラウンドとして、辞書形式で引数を渡します。
なお、インプットデータが不要なときは
client.start_execution(stateMachineArn='arn:aws:states:eu-west-1:123456789012:stateMachine:foo')
とスッキリ書けます。
引数 | 意味 |
StateMachineArn | ステートマシンのARN |
Name | 実行名。リージョン毎にユニーク。省略すると、サーバー側がユニークな名前を付与。 |
Input | ステートマシン実行用のインプット |
実行結果を確認します。
import boto3 client = boto3.client('stepfunctions') client.describe_execution( executionArn='arn:aws:states:eu-west-1:123456789012:execution:foo:1ef5b54a-e3d5-44c4-a83f-cdf8dd87059e' )
RESPONSE
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '346', 'content-type': 'application/x-amz-json-1.0', 'x-amzn-requestid': 'e916cbbc-c4a2-11e6-9ffd-2567445d8c02'}, 'HTTPStatusCode': 200, 'RequestId': 'e916cbbc-c4a2-11e6-9ffd-2567445d8c02', 'RetryAttempts': 0}, u'executionArn': u'arn:aws:states:eu-west-1:123456789012:execution:foo:1ef5b54a-e3d5-44c4-a83f-cdf8dd87059e', u'input': u'{}', u'name': u'1ef5b54a-e3d5-44c4-a83f-cdf8dd87059e', u'output': u'"Hello World!"', u'startDate': datetime.datetime(2016, 12, 17, 22, 48, 37, 190000, tzinfo=tzlocal()), u'stateMachineArn': u'arn:aws:states:eu-west-1:123456789012:stateMachine:foo', u'status': u'SUCCEEDED', u'stopDate': datetime.datetime(2016, 12, 17, 22, 48, 37, 312000, tzinfo=tzlocal())}
"status": "SUCCEEDED"
と実行が成功したことがわかります。
"output": "\"Hello World!\""
と JSON で定義したとおりのアウトプットとなっています。
AMC の下図にあるようなステートの遷移は GetExecutionHistory
API で取得します。
import boto3 client = boto3.client('stepfunctions') client.get_execution_history( executionArn='arn:aws:states:eu-west-1:123456789012:execution:foo:1ef5b54a-e3d5-44c4-a83f-cdf8dd87059e', reverseOrder=False) )
RESPONSE
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '893', 'content-type': 'application/x-amz-json-1.0', 'x-amzn-requestid': '4cfc5fae-c4a3-11e6-8f47-99e5d3d2e683'}, 'HTTPStatusCode': 200, 'RequestId': '4cfc5fae-c4a3-11e6-8f47-99e5d3d2e683', 'RetryAttempts': 0}, u'events': [{u'executionStartedEventDetails': {u'input': u'{}', u'roleArn': u'arn:aws:iam::123456789012:role/service-role/StatesExecutionRole-eu-west-1'}, u'id': 1, u'previousEventId': 0, u'timestamp': datetime.datetime(2016, 12, 17, 22, 48, 37, 190000, tzinfo=tzlocal()), u'type': u'ExecutionStarted'}, {u'id': 2, u'previousEventId': 0, u'stateEnteredEventDetails': {u'input': u'{}', u'name': u'HelloWorld'}, u'timestamp': datetime.datetime(2016, 12, 17, 22, 48, 37, 312000, tzinfo=tzlocal()), u'type': u'PassStateEntered'}, {u'id': 3, u'previousEventId': 2, u'stateExitedEventDetails': {u'name': u'HelloWorld', u'output': u'"Hello World!"'}, u'timestamp': datetime.datetime(2016, 12, 17, 22, 48, 37, 312000, tzinfo=tzlocal()), u'type': u'PassStateExited'}, {u'executionSucceededEventDetails': {u'output': u'"Hello World!"'}, u'id': 4, u'previousEventId': 3, u'timestamp': datetime.datetime(2016, 12, 17, 22, 48, 37, 312000, tzinfo=tzlocal()), u'type': u'ExecutionSucceeded'}]}
ListExecutions
API を使うと、ステートマシンの実行一覧を取得できます。
import boto3 client = boto3.client('stepfunctions') client.list_executions( stateMachineArn='arn:aws:states:eu-west-1:123456789012:stateMachine:foo' )
RESPONSE
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '323', 'content-type': 'application/x-amz-json-1.0', 'x-amzn-requestid': '06dad61b-c502-11e6-962a-27b1fe024b21'}, 'HTTPStatusCode': 200, 'RequestId': '06dad61b-c502-11e6-962a-27b1fe024b21', 'RetryAttempts': 0}, u'executions': [{u'executionArn': u'arn:aws:states:eu-west-1:123456789012:execution:foo:28c88d3b-9d58-4d23-840d-34329fe1951e', u'name': u'28c88d3b-9d58-4d23-840d-34329fe1951e', u'startDate': datetime.datetime(2016, 12, 18, 10, 8, 10, 135000, tzinfo=tzlocal()), u'stateMachineArn': u'arn:aws:states:eu-west-1:123456789012:stateMachine:foo', u'status': u'SUCCEEDED', u'stopDate': datetime.datetime(2016, 12, 18, 10, 8, 10, 182000, tzinfo=tzlocal())}]}
3. ステートマシンの削除
最後に作成したステートマシンを DeleteStateMachine
API で削除します。
import boto3 client = boto3.client('stepfunctions') client.delete_state_machine( stateMachineArn='arn:aws:states:eu-west-1:123456789012:stateMachine:foo' )
RESPONSE
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '2', 'content-type': 'application/x-amz-json-1.0', 'x-amzn-requestid': '578a70f2-c4a4-11e6-8779-db475dfb3ad2'}, 'HTTPStatusCode': 200, 'RequestId': '578a70f2-c4a4-11e6-8779-db475dfb3ad2', 'RetryAttempts': 0}}
削除したステートマシンを describe すると "status" : "DELETING"
となっています。
import boto3 client = boto3.client('stepfunctions') client.describe_state_machine( stateMachineArn='arn:aws:states:eu-west-1:123456789012:stateMachine:foo' )
RESPONSE
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '520', 'content-type': 'application/x-amz-json-1.0', 'x-amzn-requestid': '82cce048-c502-11e6-b890-d946f76fd0d2'}, 'HTTPStatusCode': 200, 'RequestId': '82cce048-c502-11e6-b890-d946f76fd0d2', 'RetryAttempts': 0}, u'creationDate': datetime.datetime(2016, 12, 18, 10, 14, 58, 828000, tzinfo=tzlocal()), u'definition': u'{\n "Comment": "A Hello World example of the Amazon States Language using an AWS Lambda Function",\n "StartAt": "HelloWorld",\n "States": {\n "HelloWorld": {\n "Type": "Pass",\n "Result": "Hello World!",\n "End": true\n }\n }\n}\n', u'name': u'foo', u'roleArn': u'arn:aws:iam::123456789012:role/service-role/StatesExecutionRole-eu-west-1', u'stateMachineArn': u'arn:aws:states:eu-west-1:123456789012:stateMachine:foo', u'status': u'DELETING'}
しばらく待つと、ステートマシンの削除が完了します。
削除完了後に DescribeStateMachine
API を実行すると、次のように StateMachineDoesNotExist
エラーが発生します。
import boto3 client = boto3.client('stepfunctions') client.describe_state_machine( stateMachineArn='arn:aws:states:eu-west-1:123456789012:stateMachine:foo' )
RESPONSE
ClientError: An error occurred (StateMachineDoesNotExist) when calling the DescribeStateMachine operation: State Machine Does Not Exist: 'arn:aws:states:eu-west-1:123456789012:stateMachine:foo'
まとめ
AWS Step Functions を AWS SDK for Python から使う方法を紹介しました。 何かのイベントをトリガーに Lambda 関数を呼び出し、Lambda 内から AWS Step Functions を呼び出すようなケースでは有用ではないかと思います。